/*
**************************************************************************************
*Myriad Engine                                                                       *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
package leola.types;

import java.lang.reflect.Method;

import leola.utils.ClassUtil;
import leola.utils.Pair;


/**
 * @author Tony
 *
 */
public class LeoNativeFunction extends LeoObject {

	/**
	 * Owner class
	 */
	private Class<?> ownerClass;
	
	/**
	 * Parameters
	 */
	private Class<?>[] paramClasses;
	
	/**
	 * Name of the function
	 */
	private String functionName;
	
	/**
	 * Owner of this method
	 */
	private Object ownerInstance;
	
	/**
	 * Method
	 */
	private Method method;
	
	/**
	 * @param ownerInstance
	 * @param method
	 */
	public LeoNativeFunction(Object ownerInstance, Method method) {
		this(ownerInstance, method.getDeclaringClass(), method.getParameterTypes(), method.getName());
		this.method = method;
	}
	
	/**
	 * @param ownerInstance
	 * @param ownerClass
	 * @param paramClasses
	 * @param functionName
	 */
	public LeoNativeFunction(Object ownerInstance, Class<?> ownerClass, Class<?>[] paramClasses, String functionName) {
		super(LeoType.NATIVE_FUNCTION);
		
		this.ownerInstance = ownerInstance;
		this.ownerClass = ownerClass;
		this.paramClasses = paramClasses;
		this.functionName = functionName;
	}
	
	/**
	 * @param ownerClass
	 * @param paramClasses
	 */
	public LeoNativeFunction(Class<?> ownerClass, Class<?>[] paramClasses, String functionName) {
		this(null, ownerClass, paramClasses, functionName);
	}
	
	/**
	 * Invokes this method.
	 * 
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public Object invoke(LeoObject ... params) throws Exception {
		Object result = null;
		
		if ( this.method == null ) {
			Pair<Method, Object> pairResult = ClassUtil.invokeMethod(this.ownerClass, this.functionName, this.ownerInstance, params);
			this.method = pairResult.getFirst();
			result = pairResult.getSecond();
		}
		else {
			result = ClassUtil.invokeMethod(this.method, this.ownerInstance, params);
		}
		return result;
	}
	
	/**
	 * Invokes a method.
	 * 
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public Object invoke(Object ... params) throws Exception {
		Object result = null;
		
		if ( this.method == null ) {
			Pair<Method, Object> pairResult = ClassUtil.invokeMethod(this.ownerClass, this.functionName, this.ownerInstance, params);
			this.method = pairResult.getFirst();
			result = pairResult.getSecond();
		}
		else {
			result = ClassUtil.invokeMethod(this.method, this.ownerInstance, params);
		}
		return result;
	}
	
	/**
	 * @return the ownerInstance
	 */
	public Object getOwnerInstance() {
		return ownerInstance;
	}
	
	/**
	 * @param ownerInstance the ownerInstance to set
	 */
	public void setOwnerInstance(Object ownerInstance) {
		this.ownerInstance = ownerInstance;
	}
	
	/**
	 * @return the method
	 */
	public Method getMethod() {
		return method;
	}
	
	/**
	 * @return the ownerClass
	 */
	public Class<?> getOwnerClass() {
		return ownerClass;
	}
	
	/**
	 * @return the paramClasses
	 */
	public Class<?>[] getParamClasses() {
		return paramClasses;
	}

	/**
	 * @return the functionName
	 */
	public String getFunctionName() {
		return functionName;
	}
	
	/* (non-Javadoc)
	 * @see leola.types.LeoObject#eq(leola.types.LeoObject)
	 */
	@Override
	public boolean eq(LeoObject other) {
		return (other != null ) && (other == this);
	}

	/* (non-Javadoc)
	 * @see leola.types.LeoObject#gt(leola.types.LeoObject)
	 */
	@Override
	public boolean gt(LeoObject other) {
		return false;
	}

	/* (non-Javadoc)
	 * @see leola.types.LeoObject#lt(leola.types.LeoObject)
	 */
	@Override
	public boolean lt(LeoObject other) {
		return false;
	}

	/* (non-Javadoc)
	 * @see leola.types.LeoObject#getValue()
	 */
	@Override
	public Object getValue() {
		return this;
	}
	
	/* (non-Javadoc)
	 * @see leola.types.LeoObject#clone()
	 */
	@Override
	public LeoObject clone() {
		LeoNativeFunction nFunction = null;
		if ( this.method != null ) {
			nFunction = new LeoNativeFunction(this.ownerInstance, this.method);
		}
		else {
			nFunction = new LeoNativeFunction(this.ownerClass, this.paramClasses, this.functionName);
		}
		return nFunction;
	}
}
